﻿using DZParallelLib.Utility;
using System;
using System.Drawing;
using System.IO;
using System.Threading.Tasks;

namespace DZParallelLib.DZImage
{
    /// <summary>
    /// Represents an already rendered DZ image. We can pull the info we need
    /// from the XML file that specifies where the DZ image is.
    /// </summary>
    internal class DZRenderedImage : DZImageBase
    {
        /// <summary>
        /// The image's XML file which contains all the info about the image itself.
        /// </summary>
        /// <param name="dzXMLFile"></param>
        public DZRenderedImage(FileInfo dzXMLFile)
        {
            var imInfo = DZImageIO.LoadImageDZFile(dzXMLFile);

            TileSize = imInfo.TileSize;
            Width = imInfo.Size.Width;
            Height = imInfo.Size.Height;
            Overlap = imInfo.Overlap;

            TileImagesDirectory = new DirectoryInfo(Path.Combine(dzXMLFile.DirectoryName, Path.GetFileNameWithoutExtension(dzXMLFile.Name) + "_files"));

            NumberOfLevels = (uint)Math.Ceiling(Math.Log(MaxDimension, 2)) + 1;
        }

        /// <summary>
        /// Returns the bit map of the image as best we can at a given size. If the image is smaller than the
        /// requested size, will return the smaller size.
        /// </summary>
        /// <param name="minimumImageSize">Minimum size in pixels of image to be returned.</param>
        /// <returns>A bitmap containing the image at close to the resolution requested as possible.</returns>
        /// <remarks>
        /// If you specify an image larger than the max size, you will get back the raw image resolution!
        /// This code won't scale the image up in size!
        /// </remarks>
        public async Task<Bitmap> GetClosestBitmap(ulong minimumImageSize)
        {
            //
            // What level is this?
            //

            if (minimumImageSize > MaxDimension)
                minimumImageSize = MaxDimension;
            var level = LevelThatContainsSize(minimumImageSize);

            //
            // Load an image from a particular level
            //

            return await LoadImageAtLevel(level);
        }

        /// <summary>
        /// Return a the complete bit map for this image rendered at a given level.
        /// </summary>
        /// <param name="level"></param>
        /// <returns></returns>
        private async Task<Bitmap> LoadImageAtLevel(uint level)
        {
            var sz = SizeOfImageInLevel(level);
            var bm = new Bitmap(sz.Width, sz.Height);
            using (var g = Graphics.FromImage(bm))
            {

                //
                // Now, load all the images for that file.
                //

                foreach (var i in ImageDescriptorAtLevel(level).Images)
                {
                    var f = new FileInfo(Path.Combine(Path.Combine(TileImagesDirectory.FullName, level.ToString(), i.Name)));
                    await f.LoadImage(tileImage => g.DrawImage(tileImage, i.X, i.Y));
                }
            }

            return bm;
        }

        /// <summary>
        /// Directory where the files exit
        /// </summary>
        public DirectoryInfo TileImagesDirectory { get; private set; }
    }
}
